package thread.philosopher;

import java.util.Random;

import thread.invariantframework.Timeout;

class Chopstick {
	private static int counter = 0;

	private int number = counter++;

	@Override
	public String toString() {
		return "Chopstick" + number;
	}
}

class Philosopher extends Thread {
	private static Random rand = new Random();

	private static int counter = 0;

	private int number = counter++;

	private Chopstick leftChopstick;

	private Chopstick rightChopstick;

	static int ponder = 0; // Package access

	public Philosopher(Chopstick left, Chopstick right) {
		this.leftChopstick = left;
		this.rightChopstick = right;
		start();
	}

	public void think() {
		System.out.println(this + " thinking");
		if (ponder > 0) {
			try {
				sleep(rand.nextInt(ponder));
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
		}
	}

	public void eat() {
		synchronized (leftChopstick) {
			System.out.println(this + " has " + this.leftChopstick
					+ " Waiting for " + this.rightChopstick);
			//yield();
			synchronized (rightChopstick) {
				System.out.println(this + " eating");
			}
		}
	}

	@Override
	public String toString() {
		return "Philosopher " + number;
	}

	public void run() {
		while (true) {
			think();
			eat();
		}
	}
}

public class DiningPhilosophers {
	public static void main(String[] args) {
		if (args.length < 3) {
			System.err.println("usage:\n"
					+ "java DiningPhilosophers numberOfPhilosophers "
					+ "ponderFactor deadlock timeout\n"
					+ "A nonzero ponderFactor will generate a random"
					+ "sleep time during think().\n"
					+ "If deadlock is not the string "
					+ "'deadlock', the program will not deadlock.\n"
					+ "A nonzero timeout will stop the program after"
					+ "that number of seconds.");
			System.exit(1);
		}

		Philosopher[] philosopher = new Philosopher[Integer.parseInt(args[0])];
		Philosopher.ponder = Integer.parseInt(args[1]);

		Chopstick left = new Chopstick();
		Chopstick right = new Chopstick();
		Chopstick first = left;

		int i = 0;
		while (i < philosopher.length - 1) {
			philosopher[i++] = new Philosopher(left, right);
			left = right;
			right = new Chopstick();
		}

		if (args[2].equals("deadlock"))
			philosopher[i] = new Philosopher(left, first);
		else
			// Swapping values prevents deadlock:
			philosopher[i] = new Philosopher(first, left);
		// Optinally break out of program:
		if (args.length >= 4) {
			int delay = Integer.parseInt(args[3]);
			new Timeout(delay * 1000, "Timed out");
		}
	}
}
